# -*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import math
from benchmarks import *
import random
from EstimatedConvergencePoint import EstimatedConvergencePoint
import os

def getweight():
    # 惯性权重
    weight = 1
    return weight

def getlearningrate():
    # 分别是粒子的个体和社会的学习因子，也称为加速常数
    lr = (1.4962,1.4962)
    return lr

def getmaxgen():
    # 最大迭代次数
    maxgen = 1000
    return maxgen

def getsizepop():
    # 种群规模
    sizepop = 100
    return sizepop

def getrangepop(dim):
    # 粒子的位置的范围限制,x、y方向的限制相同
    # rangepop = (-2*math.pi , 2*math.pi)
    rangepop = [(-100, 100)] * dim
    return np.array(rangepop)

def getrangespeed(dim):
    # 粒子的速度范围限制
    rangespeed = [(-100, 100)] * dim
    return np.array(rangespeed)

# def func(x):
#     # x输入粒子位置
#     # y 粒子适应度值
#     if (x[0]==0)&(x[1]==0):
#         y = np.exp((np.cos(2*np.pi*x[0])+np.cos(2*np.pi*x[1]))/2)-2.71289
#     else:
#         y = np.sin(np.sqrt(x[0]**2+x[1]**2))/np.sqrt(x[0]**2+x[1]**2)+np.exp((np.cos(2*np.pi*x[0])+np.cos(2*np.pi*x[1]))/2)-2.71289
#     return y

def initpopvfit(sizepop, dim=2):
    pop = np.zeros((sizepop,dim))
    v = np.zeros((sizepop,dim))
    fitness = np.zeros(sizepop)

    for i in range(sizepop):
        for j in range(len(rangepop)):
            pop[i][j] = (np.random.uniform(rangepop[j][0], rangepop[j][1]))
            # pop[i] = [(np.random.rand()-0.5)*rangepop[0]*2,(np.random.rand()-0.5)*rangepop[1]*2]
            v[i][j] = (np.random.uniform(rangespeed[j][0], rangespeed[j][1]))
            # v[i] = [(np.random.rand()-0.5)*rangepop[0]*2,(np.random.rand()-0.5)*rangepop[1]*2]
        fitness[i] = func(pop[i])
    return pop,v,fitness

def getinitbest(fitness,pop):
    # 群体最优的粒子位置及其适应度值
    gbestpop,gbestfitness = pop[fitness.argmin()].copy(),fitness.min()
    #个体最优的粒子位置及其适应度值,使用copy()使得对pop的改变不影响pbestpop，pbestfitness类似
    pbestpop,pbestfitness = pop.copy(),fitness.copy()

    return gbestpop,gbestfitness,pbestpop,pbestfitness

function_nums = 18
filename = "results\\gbest_basic.txt"
with open(filename, 'w') as ff:
    print('file created successfully or file has existed')
for num in range(1, function_nums + 1):
    if num == 1:
        func = F1  # cost_func
    elif num == 2:
        func = F2
    elif num == 3:
        func = F3
    elif num == 4:
        func = F4
    elif num == 5:
        func = F5
    elif num == 6:
        func = F6
    elif num == 7:
        func = F7
    elif num == 8:
        func = F8
    elif num == 9:
        func = F9
    elif num == 10:
        func = F10
    elif num == 11:
        func = F11
    elif num == 12:
        func = F12
    elif num == 13:
        func = F13
    elif num == 14:
        # func = F14
        continue
    elif num == 15:
        func = F15
    elif num == 16:
        func = F16
    elif num == 17:
        func = F17
    elif num == 18:
        func = F18
    elif num == 19:
        # func = F19
        continue
    elif num == 20:
        func = F20
        # continue
    elif num == 21:
        func = F21
    elif num == 22:
        func = F22
    elif num == 23:
        func = F23
    print(func.__name__)
    # func = F3
    iter_nums = 5
    results_iters = []
    solution_iters = []
    dim = 30
    MAX_NFC = 10000 * dim
    for iters in range(iter_nums):
        # print('generations:', genarations)
        NFC = 0
        w = getweight()
        lr = getlearningrate()
        maxgen = getmaxgen()
        sizepop = getsizepop()
        rangepop = getrangepop(dim) # Bounds [(x1_min, x1_max), (x2_min, x2_max),...]
        up_pop = np.tile(rangepop[:, 1], sizepop).reshape(sizepop, dim)
        low_pop = np.tile(rangepop[:, 0], sizepop).reshape(sizepop, dim)
        rangespeed = getrangespeed(dim)
        up_speed = np.tile(rangespeed[:, 1], sizepop).reshape(sizepop, dim)
        low_speed = np.tile(rangespeed[:, 0], sizepop).reshape(sizepop, dim)


        trial_runs = 30                        # Max number of trial runs (maxiter)

        pop,v,fitness = initpopvfit(sizepop, dim)
        NFC += sizepop
        gbestpop,gbestfitness,pbestpop,pbestfitness = getinitbest(fitness,pop)

        # result = np.zeros(maxgen)
        result = []
        genarations = 0
        # for i in range(maxgen):
        while NFC <= MAX_NFC:
            genarations += 1
            last_pop = pop.copy()
            t=0.5
            #群体平均粒子位置及其适应值
            # mbestpop = np.mean(pop, axis=0)
            mbestpop = np.mean(pbestpop, axis=0)
            mbestfitness = func(mbestpop)
            #速度更新
            for j in range(sizepop):
                v[j] += lr[0]*np.random.rand()*(pbestpop[j]-pop[j])+lr[1]*np.random.rand()*(gbestpop-pop[j])
                # v[j] = w * v[j] + lr[0]*np.random.rand()*(pbestpop[j]-pop[j])+lr[1]*np.random.rand()*(mbestpop-pop[j])
            v[v<rangespeed[:, 0]] = low_speed[v<rangespeed[:, 0]]
            v[v>rangespeed[:, 1]] = up_speed[v>rangespeed[:, 1]]

            #粒子位置更新
            for j in range(sizepop):
                # pop[j] += 0.5*v[j]
                # pop[j] = t*(0.5*v[j])+(1-t)*pop[j]
                pop[j] += v[j]
            # pop[pop<rangepop[0]] = rangepop[0]
            # pop[pop>rangepop[1]] = rangepop[1]
            
            low_idx = pop<rangepop[:, 0]
            up_idx = pop>rangepop[:, 1]
            pop[low_idx] = low_pop[low_idx]
            pop[up_idx] = up_pop[up_idx]

            #适应度更新
            for j in range(sizepop):
                fitness[j] = func(pop[j])
            NFC += sizepop

            for j in range(sizepop):
                if fitness[j] < pbestfitness[j]:
                    pbestfitness[j] = fitness[j]
                    pbestpop[j] = pop[j].copy()
                #---Estimate convergence point-------------------+
            moving_vector = np.array(pop) - np.array(last_pop)
            convergencePoint = EstimatedConvergencePoint(np.array(last_pop), moving_vector)
            if type(convergencePoint) is not int:
                score_convergencePoint = func(np.squeeze(convergencePoint))
                NFC += 1
                rank_convergencePoint = sum(score_convergencePoint > fitness)
                # print('       > convergence point rank:', rank_convergencePoint, '\n')


                #---replace the worst point in the population with the estimated convergence point----------------+
                if max(fitness) > score_convergencePoint:
                    idx_worst = np.argmax(fitness)
                    pop[idx_worst] = list(np.squeeze(convergencePoint))
                    fitness[idx_worst] = score_convergencePoint
                    if fitness[idx_worst] < pbestfitness[idx_worst]:
                        pbestfitness[idx_worst] = fitness[idx_worst]
                        pbestpop[idx_worst] = pop[idx_worst].copy()

            if pbestfitness.min() < gbestfitness :
                gbestfitness = pbestfitness.min()
                gbestpop = pop[pbestfitness.argmin()].copy()

            # result[genarations - 1] = gbestfitness
            result.append(gbestfitness)
        # print('generations', genarations)
        result_temp = np.array(result)
        if iters == 0:
            result_all = np.zeros_like(result_temp)
        length = min(len(result_all), len(result_temp))
        result_all[:length] += result_temp[:length]
        results_iters.append(gbestfitness)
        solution_iters.append(gbestpop)
    result_all /= iter_nums
    array_name = func.__name__ + '_gbest_basic'
    np.save(array_name, result_all[:length])
    # print('best solusion:', gbestpop)
    # print('best fitness:', gbestfitness)
    print('best solusion:', solution_iters[np.argmin(results_iters)])
    print('best fitness:', min(results_iters))
    print('mean fitness', np.mean(results_iters))
    with open(filename,"a+") as file:
        file.write(func.__name__)
        file.write('\n')
        file.write('best solution: ' + str(solution_iters[np.argmin(results_iters)]))
        file.write('\n')
        file.write('best fitness: ' + str(min(results_iters)))
        file.write('\n')
        file.write('mean fitness: ' + str(np.mean(results_iters)))
        file.write('\n')
    # plt.plot(result)
    # plt.show()

